package cn.fl.project.service.impl;

import cmm.comm.facade.framework.api.ISysDictionaryApiService;
import cmm.comm.facade.framework.dto.SysDictionaryDTO;
import cn.cmm.base.framework.constant.BaseConstant;
import cn.cmm.base.framework.exception.BaseException;
import cn.cmm.base.framework.utils.CollectionUtils;
import cn.cmm.base.framework.utils.DateUtils;
import cn.cmm.base.framework.utils.DecimalUtils;
import cn.cmm.base.framework.utils.StringUtils;
import cn.fl.capital.api.ICapCstSettleApiService;
import cn.fl.capital.condition.CapCstStmApplyQC;
import cn.fl.capital.condition.CapCstStmScheduleQC;
import cn.fl.capital.constant.SettleConstant;
import cn.fl.capital.constant.ZGSettleScheduleEnum;
import cn.fl.capital.dto.CapCstStmApplyDTO;
import cn.fl.capital.dto.CapCstStmScheduleDTO;
import cn.fl.framework.base.context.CurrentThreadContext;
import cn.fl.framework.constant.DictionaryConstant;
import cn.fl.project.api.IPrjSettleOfFinaInstApiService;
import cn.fl.project.condition.PrjPrdPaySchMQueryCondition;
import cn.fl.project.constant.PrjPrdPaySchDConstant;
import cn.fl.project.constant.PrjPrdPaySchMConstant;
import cn.fl.project.constant.PrjQuatPrpsConstant;
import cn.fl.project.constant.PrjQuatPrpsFeeConstant;
import cn.fl.project.dto.PrjPrdPaySchDDTO;
import cn.fl.project.dto.PrjPrdPaySchMDTO;
import cn.fl.project.dto.PrjPrdPrpsFeeDetailDTO;
import cn.fl.project.dto.PrjStlMDTO;
import cn.fl.project.service.IPrjPrdPaySchMApiService;
import cn.fl.project.service.IPrjSettleOfFinInstService;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class PrjSettleOfFinInstServiceImpl implements IPrjSettleOfFinInstService {

    private Logger logger = LoggerFactory.getLogger(PrjSettleOfFinInstServiceImpl.class);

    @Autowired
    private IPrjSettleOfFinaInstApiService prjSettleOfFinaInstApiService;
    @Autowired
    private IPrjPrdPaySchMApiService prjPrdPaySchMApiService;
    @Autowired
    private ISysDictionaryApiService sysDictionaryApiService;
    @Autowired
    private ICapCstSettleApiService capCstSettleApiService;

    @Override
    public SysDictionaryDTO isSupport(String bizFlag) throws BaseException {
        return prjSettleOfFinaInstApiService.isSupport(bizFlag);
    }

    @Override
    public int isHaveInterface(String bizFlag) throws BaseException {
        try {
            SysDictionaryDTO support = prjSettleOfFinaInstApiService.isSupport(bizFlag);
            if (support != null) {
                // 是否提供第三方接口，1-是，0-否
                String codeAttr1 = support.getCodeAttr1();
                return Integer.parseInt(codeAttr1);
            }
        } catch (Exception e) {
            logger.error("查询资方[" + bizFlag + "]是否提供第三方接口失败，cause：", e);
        }
        return 0;
    }

    @Override
    public boolean checkApplyConstraints(Long paySchId, String settleType) throws BaseException {
        // paySchId 为 我司支付表ID
        return prjSettleOfFinaInstApiService.checkApplyConstraints(paySchId, null, settleType);
    }

    @Override
    public Integer getStartSettlePd(Long paySchId) throws BaseException {
        return prjSettleOfFinaInstApiService.getStartSettlePd(paySchId);
    }

    @Override
    public PrjPrdPaySchMDTO getCalculateSettleData(Long prjId, Long paySchId, Integer settPayPd, String settleType) throws BaseException {
        // 当前时间yyyy-MM-dd
        Date currentDate = DateUtils.getCurrentDate(null);
        String sysCd = CurrentThreadContext.getCurrentSysCd();
        String orgCd = CurrentThreadContext.getCurrentOrgCd();

        PrjStlMDTO prjStlMDTO = null;
        boolean inFlow = (prjId == null && paySchId != null) ? true : false;
        if (inFlow) {
            // 此情况代表是流程中过来的请求
            // 注意：此处的paySchId有可能是我司的支付表ID，如果是我司的支付表ID，则需要查询出资方支付表ID
            PrjPrdPaySchMDTO schMDTO = prjPrdPaySchMApiService.selectPrjPrdPaySchMById(paySchId);
            prjId = schMDTO.getPrjId();
            currentDate = null;

            if (schMDTO.getIsSqBiz() == BaseConstant.IS_YESNO_YES) {
                paySchId = getZfPaySchIdByPrjId(prjId);
                if (paySchId == null) {
                    throw new BaseException("资方支付表信息不存在！");
                }
            }

            prjStlMDTO = prjPrdPaySchMApiService.selectByPaySchMId(paySchId, PrjPrdPaySchMConstant.PAY_SCH_TYP_CD_CST);
            if (StringUtils.isEmpty(settleType)) {
                CapCstStmApplyDTO capCstStmApplyDTO = capCstSettleApiService.selectTheNewestApply(paySchId);
                settleType = capCstStmApplyDTO.getTypeCd();
            }
        }
        if (StringUtils.isEmpty(settleType)) {
            throw new BaseException("结清类型不能为空！");
        }
        PrjPrdPaySchMDTO pay = null;
        if ("3".equals(settleType)) {
            // 判断是返回正常结清数据还是提前结清数据
            pay = decideSettleType(prjId, currentDate, settPayPd);
        } else {
            pay = prjSettleOfFinaInstApiService.getCalculateSettleData(prjId, currentDate, settPayPd, settleType, sysCd, orgCd);
            pay.setSettleType(settleType);
        }

        // 如果prj_stl_m表中有数据，替换掉相应的金额数据
        if (prjStlMDTO != null) {
            if (null != prjStlMDTO.getRmS03ArTotAmt()) {
                pay.setLastPrintAmt(prjStlMDTO.getRmS03ArTotAmt() - (prjStlMDTO.getStlOverdueS03Amt() == null ? 0 : prjStlMDTO.getStlOverdueS03Amt()));
            }
            if (null != prjStlMDTO.getRmS02ArTotAmt()) {
                pay.setLastIntAmt(prjStlMDTO.getRmS02ArTotAmt() - (prjStlMDTO.getStlOverdueS02Amt() == null ? 0 : prjStlMDTO.getStlOverdueS02Amt()));
            }
            if (null != prjStlMDTO.getStlPenaltyAmt()) {
                pay.setPenaltyAmt(prjStlMDTO.getStlPenaltyAmt());
            }
            if (null != prjStlMDTO.getStlOverdueAmt()) {
                pay.setOverdueAmt(prjStlMDTO.getStlOverdueAmt());
            }
            if (null != prjStlMDTO.getStlAmt()) {
                pay.setStlAmt(prjStlMDTO.getStlAmt().longValue());
                pay.setFinalPayAmt(prjStlMDTO.getStlAmt());
                pay.setTotalSettAmt(prjStlMDTO.getStlAmt());
            }
        }

        mergePaySchFee(pay, sysCd, orgCd);

        if (SettleConstant.CODE_SETT_TYPE_ADVANCE.equals(settleType)) {
            mergePaySchD(pay);
        }

        if (!inFlow) {
            // 结清预览页面，计算结清优惠金额1，结清金额减去结清优惠金额1
            Map<String, Double> irrMap = prjPrdPaySchMApiService.getCalcStlCstIRRAndDiscountForAnyPaySchId(pay.getId(), pay.getStartLsPd(),
                    pay.getTotalSettAmt().longValue(), pay.getApplyDt());
            Double irr = irrMap.get("irr");
            Double discountAmtIRR = irrMap.get("discountAmtIRR");
            pay.setCstStlIrr(irr);
            pay.setDiscountAmt1(discountAmtIRR);

            pay.setTotalSettAmt(pay.getTotalSettAmt() - discountAmtIRR);
            pay.setFinalPayAmt(pay.getTotalSettAmt());
        }

        return pay;
    }

    /**
     * 根据项目ID获取资方支付表ID
     *
     * @param prjId
     * @return
     * @throws BaseException
     */
    private Long getZfPaySchIdByPrjId(Long prjId) throws BaseException {
        PrjPrdPaySchMQueryCondition queryCondition = new PrjPrdPaySchMQueryCondition();
        queryCondition.setPrjId(prjId);
        queryCondition.setPaySchTypCd(PrjPrdPaySchMConstant.PAY_SCH_TYP_CD_ZF);
        List<PrjPrdPaySchMDTO> prjPrdPaySchMDTOS = prjPrdPaySchMApiService.selectListByPrjId(queryCondition);
        for (PrjPrdPaySchMDTO prjPrdPaySchMDTO : prjPrdPaySchMDTOS) {
            if (prjPrdPaySchMDTO.getIsSqBiz() == BaseConstant.IS_YESNO_NO) {
                return prjPrdPaySchMDTO.getId();
            }
        }
        return null;
    }

    /**
     * 处理我司支付表中已付款项列表数据
     *
     * @param pay
     * @param sysCd
     * @param orgCd
     * @throws BaseException
     */
    private void mergePaySchFee(PrjPrdPaySchMDTO pay, String sysCd, String orgCd) throws BaseException {
        if (CollectionUtils.isNotEmpty(pay.getFeeDetailList())) {
            int feeSize = pay.getFeeDetailList().size();
            Map<String, PrjPrdPaySchDDTO> mergeMap = Maps.newHashMapWithExpectedSize(feeSize / 2);

            Map<String, String> feeTypCdMap = this.sysDictionaryApiService.searchGroupToMap(sysCd, orgCd, DictionaryConstant.CODE_PRJ_QUAT_FEE_ALL_TYP_CD);
            PrjPrdPaySchDDTO mergePaySchD = null;
            for (PrjPrdPaySchDDTO prjPrdPaySchDTO : pay.getFeeDetailList()) {
                String feeTypCd = prjPrdPaySchDTO.getFeeTypCd();
                if (feeTypCdMap.containsKey(feeTypCd)) {
                    prjPrdPaySchDTO.setFeeTypCdNm(feeTypCdMap.get(feeTypCd));
                }
                // PRJ0039，剔除尾款费用项，只在应还项目信息显示
                if (PrjQuatPrpsConstant.CODE_PRJ_QUAT_FEE_CLS_TYP_PRPS.equals(prjPrdPaySchDTO.getFeeClsCd())
                        && !PrjQuatPrpsFeeConstant.FEE_TYP_CD_62.equals(feeTypCd)) {
                    if (mergeMap.containsKey(feeTypCd)) {
                        mergePaySchD = mergeMap.get(feeTypCd);
                        mergePaySchD.setFeeAmt(DecimalUtils.add(mergePaySchD.getFeeAmt(), prjPrdPaySchDTO.getFeeAmt()));
                        mergePaySchD.setPaidUpAmt(mergePaySchD.getPaidUpAmt() + prjPrdPaySchDTO.getPaidUpAmt());
                    } else {
                        mergePaySchD = new PrjPrdPaySchDDTO();
                        mergePaySchD.setFeeTypCd(feeTypCd);
                        mergePaySchD.setFeeTypCdNm(prjPrdPaySchDTO.getFeeTypCdNm());
                        mergePaySchD.setFeeAmt(prjPrdPaySchDTO.getFeeAmt());
                        mergePaySchD.setPaidUpAmt(prjPrdPaySchDTO.getPaidUpAmt());
                        mergeMap.put(feeTypCd, mergePaySchD);
                    }
                }
            }
            List<PrjPrdPaySchDDTO> list = Lists.newArrayListWithCapacity(feeSize);
            for (Map.Entry<String, PrjPrdPaySchDDTO> entry : mergeMap.entrySet()) {
                list.add(entry.getValue());
            }
            //首期款合计项
            PrjPrdPaySchDDTO tatal = new PrjPrdPaySchDDTO();
            tatal.setFeeTypCdNm("首期款合计");
            tatal.setFeeAmt(pay.getFirstPayAmt());
            tatal.setPaidUpAmt(pay.getTotalFirstPayAmt().longValue());
            list.add(tatal);
            pay.setFeeDetailList(list);
        }
    }

    /**
     * 处理提前结清流程中支付表中间状态数据
     *
     * @param paySchMDTO
     * @throws BaseException
     */
    private void mergePaySchD(PrjPrdPaySchMDTO paySchMDTO) throws BaseException {
        if (paySchMDTO == null) {
            return;
        }
        // 开始结清期次：到期为0
        Integer startLsPd = paySchMDTO.getStartLsPd();
        List<PrjPrdPaySchDDTO> paySchDList = paySchMDTO.getPrjPrdPaySchDList();
        if (CollectionUtils.isNotEmpty(paySchDList)) {
            //Map<Integer, PrjPrdPrpsFeeDetailDTO> prdPayMap = Maps.newHashMapWithExpectedSize(paySchDList.size() / 4);
            // getPrjPrdPaySchDList()中的数据已经按照payPd进行了正序，此处用有序链表LinkedHashMap
            Map<Integer, PrjPrdPrpsFeeDetailDTO> prdPayMap = new LinkedHashMap<>(paySchDList.size() / 4);
            Double rentAmt = 0d;
            Double interestAmt = 0d;
            Double principalAmt = 0d;
            for (PrjPrdPaySchDDTO prdPaySchDDTO : paySchDList) {
                /* REQ-7223 提前结清取消期次合并 by liurong
                if (startLsPd.intValue() != 0 && prdPaySchDDTO.getPayPd().compareTo(startLsPd) > 0) {
                    break;
                }*/
                PrjPrdPrpsFeeDetailDTO detailDTO = null;
                if (prdPayMap.containsKey(prdPaySchDDTO.getPayPd())) {
                    detailDTO = prdPayMap.get(prdPaySchDDTO.getPayPd());
                } else {
                    detailDTO = new PrjPrdPrpsFeeDetailDTO();
                    detailDTO.setPayPd(prdPaySchDDTO.getPayPd());
                    detailDTO.setIsModify(prdPaySchDDTO.getIsModify());
                    detailDTO.setPayDt(prdPaySchDDTO.getPayDt());
                }
                /* REQ-7223 提前结清取消期次合并 by liurong
                if (startLsPd.intValue() != 0 && prdPaySchDDTO.getPayPd().compareTo(startLsPd) == 0) {
                    detailDTO.setRntAmt(DecimalUtils.sub(paySchMDTO.getTotalRntAmt(), rentAmt));
                    detailDTO.setIntAmt(DecimalUtils.sub(paySchMDTO.getTotalIntAmt(), interestAmt));
                    detailDTO.setPrinAmt(DecimalUtils.sub(paySchMDTO.getTotalPrinAmt(), principalAmt));
                    detailDTO.setRemPrinAmt(0d);
                    detailDTO.setIsDel(PrjPrdPaySchDConstant.PRJ_PRD_PAY_FEE_FLOW_SAVE_STATUS);
                    prdPayMap.put(prdPaySchDDTO.getPayPd(), detailDTO);
                    break;
                } else {  }*/
                // 租金
                if (PrjQuatPrpsConstant.CODE_FEE_WAY_TYPE_RNT_PRICE.equals(prdPaySchDDTO.getFeeTypCd())) {
                    detailDTO.setRntAmt(prdPaySchDDTO.getFeeAmt());
                    rentAmt = DecimalUtils.add(prdPaySchDDTO.getFeeAmt(), rentAmt);
                }
                // 利息
                if (PrjQuatPrpsConstant.CODE_FEE_WAY_TYPE_INT_PRICE.equals(prdPaySchDDTO.getFeeTypCd())) {
                    detailDTO.setIntAmt(prdPaySchDDTO.getFeeAmt());
                    interestAmt = DecimalUtils.add(prdPaySchDDTO.getFeeAmt(), interestAmt);
                }
                // 本金
                if (PrjQuatPrpsConstant.CODE_FEE_WAY_TYPE_PRINT_PRICE.equals(prdPaySchDDTO.getFeeTypCd())) {
                    detailDTO.setPrinAmt(prdPaySchDDTO.getFeeAmt());
                    principalAmt = DecimalUtils.add(prdPaySchDDTO.getFeeAmt(), principalAmt);
                }
                // 剩余本金
                if (PrjQuatPrpsConstant.CODE_FEE_WAY_TYPE_REMPRIN_PRICE.equals(prdPaySchDDTO.getFeeTypCd())) {
                    detailDTO.setRemPrinAmt(prdPaySchDDTO.getFeeAmt());
                }
                prdPayMap.put(prdPaySchDDTO.getPayPd(), detailDTO);
            }
            List<PrjPrdPrpsFeeDetailDTO> prjPrdPayDetailList = Lists.newArrayListWithCapacity(prdPayMap.size());
            for (Map.Entry<Integer, PrjPrdPrpsFeeDetailDTO> entry : prdPayMap.entrySet()) {
                prjPrdPayDetailList.add(entry.getValue());
            }
            paySchMDTO.setPrjPrdPayDetailList(prjPrdPayDetailList);
        }
    }

    @Override
    public boolean modifyStlAmtOther(Long paySchId, Long stlAmtOther) throws BaseException {
        PrjPrdPaySchMDTO schMDTO = new PrjPrdPaySchMDTO();
        schMDTO.setId(paySchId);
        schMDTO.setStlAmtOther(stlAmtOther);
        schMDTO.setMdfTm(new Date());
        schMDTO.setMdfUsrId(CurrentThreadContext.getCurrentUserId());
        return prjPrdPaySchMApiService.modifyEntity(schMDTO);
    }

    @Override
    public boolean modifyStlDt(Long prjId, String stlDt) throws BaseException {
        return prjSettleOfFinaInstApiService.modifyStlDt(prjId, DateUtils.formatStringToDate(stlDt, DateUtils.YYYY_MM_DD));
    }

    @Override
    public List<CapCstStmScheduleDTO> selectScheduleListOfZG(Long paySchId, String pInstId) throws BaseException {
        if (StringUtils.isEmpty(pInstId)) {
            throw new BaseException("流程实例ID不能为空！");
        }
        if (paySchId == null) {
            throw new BaseException("支付表ID不能为空！");
        }
        CapCstStmApplyQC capCstStmApplyQC = new CapCstStmApplyQC();
        capCstStmApplyQC.setProcId(pInstId);
        capCstStmApplyQC.setPaySchId(paySchId);
        List<CapCstStmApplyDTO> capCstStmApplyDTOS = capCstSettleApiService.searchList(capCstStmApplyQC);
        if (CollectionUtils.isEmpty(capCstStmApplyDTOS)) {
            throw new BaseException("流程实例ID对应的申请记录不存在！");
        }
        CapCstStmScheduleQC condition = new CapCstStmScheduleQC();
        condition.setPaySchId(paySchId);
        condition.setApplyId(capCstStmApplyDTOS.get(0).getId());
        condition.setDataRemark(SettleConstant.DATA_REMARK_1);
        return capCstSettleApiService.selectCapCstStmScheduleByCondition(condition);
    }

    @Override
    public CapCstStmScheduleDTO getScheduleById(Long id) throws BaseException {
        CapCstStmScheduleQC condition = new CapCstStmScheduleQC();
        condition.setId(id);
        return prjSettleOfFinaInstApiService.getSchedule(condition);
    }

    @Override
    public int modifyScheduleOtherInfo(CapCstStmScheduleDTO record) throws BaseException {
        return prjSettleOfFinaInstApiService.modifyOtherStlAmt(record);
    }

    @Override
    public Long reacquire(Long prjId, Long paySchId, ZGSettleScheduleEnum scheduleEnum) throws BaseException {
        Long otherSettleAmt = prjSettleOfFinaInstApiService.getOtherSettleAmt(prjId, paySchId, scheduleEnum,
                CurrentThreadContext.getCurrentUserId(), CurrentThreadContext.getCurrentUserName());
        if (otherSettleAmt != null) {
            // 更新支付表中stl_amt_other
            PrjPrdPaySchMDTO paySchMDTO = new PrjPrdPaySchMDTO();
            paySchMDTO.setId(paySchId);
            paySchMDTO.setStlAmtOther(otherSettleAmt);
            prjPrdPaySchMApiService.modifyEntity(paySchMDTO);
        }
        return otherSettleAmt;
    }

    /**
     * 判断是返回正常结清数据还是提前结清数据
     * @param prjId
     * @param currentDate
     * @param settPayPd
     * @return
     */
    public PrjPrdPaySchMDTO decideSettleType(Long prjId, Date currentDate, Integer settPayPd) {
        // 因为正常结清与提前结清的唯一区别是结清优惠金额，这里先计算提前结清数据
        PrjPrdPaySchMDTO pay = prjSettleOfFinaInstApiService.getCalculateSettleData(prjId,
                currentDate,
                settPayPd,
                SettleConstant.CODE_SETT_TYPE_ADVANCE,
                CurrentThreadContext.getCurrentSysCd(),
                CurrentThreadContext.getCurrentOrgCd());

        // 当前日期距离租赁到期日<6期 or 超过最后一期还款日，走正常结清；否则比较正常结清金额和提前结清金额，哪个金额小走哪个流程
        // 取出租金列表
        List<PrjPrdPaySchDDTO> rentList = pay.getPrjPrdPaySchDList().stream()
                .filter(p -> PrjQuatPrpsConstant.CODE_FEE_WAY_TYPE_RNT_PRICE.equals(p.getFeeTypCd()))
                .collect(Collectors.toList());
        // 最后一期期次
        Integer maxPayPd = rentList.get(rentList.size() - 1).getPayPd();
        // 当期日期所在的期次
        Integer currentPd = 0;
        for (PrjPrdPaySchDDTO paySchDDTO : rentList) {
            if (paySchDDTO.getPayDt().compareTo(currentDate) > 0) {
                currentPd = paySchDDTO.getPayPd();
                break;
            }
        }

        // 正常结清金额
        Double normalSettAmt = pay.getTotalSettAmt() + pay.getDiscountAmt();
        if ((currentPd.intValue() > 0 && (maxPayPd.intValue() - currentPd.intValue() < 6))
                || currentPd.intValue() == 0) {
            // 返回正常结清
            pay.setSettleType(SettleConstant.CODE_SETT_TYPE_NORMAL);
            pay.setTotalSettAmt(normalSettAmt);
            pay.setDiscountAmt(0d);
            return pay;
        } else {
            // 比较正常结清金额和提前结清金额，哪个小返回哪个结清类型数据
            if (pay.getTotalSettAmt().doubleValue() < normalSettAmt.doubleValue()) {
                // 返回提前结清
                pay.setSettleType(SettleConstant.CODE_SETT_TYPE_ADVANCE);
                return pay;
            } else {
                // 返回正常结清
                pay.setSettleType(SettleConstant.CODE_SETT_TYPE_NORMAL);
                pay.setTotalSettAmt(normalSettAmt);
                pay.setDiscountAmt(0d);
                return pay;
            }
        }
    }

}
